package com.webdrider.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.common.dao.util.IDGenerator;
import com.common.model.action.Session;
import com.common.model.util.UserType;
import com.common.util.MemoryCache;
import com.common.util.SetCache;

public class SessionCacheContainer {

	private static final String SESSION_VALUES = "SessionValues";
	private static final SetCache<String> setCache;
	private static final MemoryCache<String, Session> sessions;
	private static final Long EXPIRE_TIME = 900000L; //15 min
	static {
		setCache = new SetCache<String>(SESSION_VALUES);
		sessions = new MemoryCache<String, Session>();
	}
	
	private SessionCacheContainer() {}
	
	public static List<Session> getAll() {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		List<Session> values = new ArrayList<Session>();

		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			values.add(iterator.next().getValue());
		}
		return values;
	}
	
	public static void addSession(Session session) {
		sessions.put(session.getId(), session);
		setCache.add(session.getId());
	}
	
	/**
	 * Create a new session with a random generated id.
	 * 
	 * @param userId
	 * @return sessionId
	 */
	public static String createSession(Long userId, UserType userType) {
		Session session = new Session();
		
		session.setId(IDGenerator.generateID());
		session.setUserId(userId);
		session.setUserType(userType);
		session.setTime(new GregorianCalendar());
		
		sessions.put(session.getId(), session);
		setCache.add(session.getId());
		
		return session.getId();
	}
	
	/**
	 * Get the session by it's sessionId. It will return null if not found.
	 * 
	 * @param id
	 * @return Session/null
	 */
	public static Session getSession(String id) {
		return sessions.get(id);
	}
	
	public static Session getSession(Long userId) {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			Session session = iterator.next().getValue();
			if (session.getUserId().equals(userId)) {
				return session;
			}
		}
		return null;
	}
	
	public static String getSessionId(Long userId) {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			Session session = iterator.next().getValue();
			if (session.getUserId().equals(userId)) {
				return session.getId();
			}
		}
		return null;
	}
	
	/**
	 * Get the user's id by it's session
	 * 
	 * @param sessionId
	 * @return userId/null
	 */
	public static Long getUserId(String id) {
		Session session = sessions.get(id);
		
		if (session != null) {
			return session.getUserId();
		} else {
			return null;
		}
	}
	
	public static UserType getUserType(String id) {
		Session session = sessions.get(id);
		
		if (session != null) {
			return session.getUserType();
		} else {
			return null;
		}
	}
	
	/**
	 * Get the session with the given userId and delete it.
	 * 
	 * @param userId
	 * @return sessionId
	 */
	public static String getAndDeleteSession(Long userId) {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			Session session = iterator.next().getValue();
			if (session.getUserId().equals(userId)) {
				String sessionId = session.getId();
				sessions.remove(session.getId());
				setCache.remove(session.getId());
				
				return sessionId;
			}
		}
		return null;
	}
	
	/**
	 * Delete all the session that are idle for more than EXPIRED_TIME time and 
	 * return a list with all of the user's id.
	 * 
	 * @return
	 */
	public static List<Long> getAndDeleteExpiredSessions() {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		List<Long> list = new ArrayList<Long>();
		Calendar calendar = new GregorianCalendar();
		
		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			Session session = iterator.next().getValue();
			if (calendar.getTimeInMillis() - session.getTime().getTimeInMillis() > EXPIRE_TIME) {
				list.add(session.getUserId());
				sessions.remove(session.getId());
				setCache.remove(session.getId());
			}
		}
		return list;
	}
	
	public static void deleteSession(Session session) {
		sessions.remove(session.getId());
		setCache.remove(session.getId());
	}
	
	public static void deleteSession(String id) {
		sessions.remove(id);
		setCache.remove(id);
	}
	
	public static void deleteSession(Long userId) {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			Session session = iterator.next().getValue();
			if (session.getUserId().equals(userId)) {
				sessions.remove(session.getId());
				setCache.remove(session.getId());
			}
		}
	}
	
	/**
	 * Delete all the sessions that are idle for more than EXPIRED_TIME time.
	 */
	public static void deleteExpiredSessions() {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		Calendar calendar = new GregorianCalendar();
		
		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			Session session = iterator.next().getValue();
			if (calendar.getTimeInMillis() - session.getTime().getTimeInMillis() > EXPIRE_TIME) {
				sessions.remove(session.getId());
				setCache.remove(session.getId());
			}
		}
	}
	
	public static int size() {
		return sessions.size();
	}
	
	/**
	 * Checks if the provided session exists in the SessionContainer
	 * 
	 * @param session
	 * @return true/false
	 */
	public static boolean contains(Session session) {
		return sessions.containtValue(session);
	}
	
	public static boolean contains(Long userId) {
		Map<String, Session> map = sessions.getAll(setCache.getValues());
		Set<Map.Entry<String, Session>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, Session>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			Session session = iterator.next().getValue();
			if (session.getUserId().equals(userId)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Checks if the session with the provided id and the provided userId 
	 * exists in the SessionContainer
	 * 
	 * @param id - session id
	 * @param userId
	 * @return true/false
	 */
	@Deprecated
	public static boolean contains(String id, Long userId) {
		Session session = sessions.get(id);
		return (session != null && session.getId().equals(userId));
	}
	
	/**
	 * Check if the session with the given id exists in the SessionContainer 
	 * and if it does, refresh it's timer.
	 * 
	 * @param id - session id
	 * @return true/false
	 */
	public static boolean containsAndRefresh(String id) {
		Session session = sessions.get(id);
		if (session != null) {
			session.setTime(new GregorianCalendar());
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Check if the session with the given id and the given userId exists in the 
	 * SessionContainer and if it does refresh it's timer.
	 * 
	 * @param id
	 * @param userId
	 * @return true/false
	 */
	@Deprecated
	public static boolean containsAndRefresh(String id, Long userId) {
		Session session = sessions.get(id);
		if (session != null && session.getUserId().equals(userId)) {
			session.setTime(new GregorianCalendar());
			return true;
		} else {
			return false;
		}
	}
}
